package org.jflame.commons.file;

import org.jflame.commons.util.IOHelper;
import org.jflame.commons.util.StringHelper;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Optional;
import java.util.StringTokenizer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * MimeType检测工具类,根据扩展名检测MimeType
 * <p>
 * 默认加载/META-INF/mime.type文件, 该文件格式与jdk1.8的javax.activation.MimeTypeFileTypeMap一样,所以如果是jdk1.8可以使用MimeTypeFileTypeMap,
 * 如果是jdk9+使用本类
 * 
 * @author charles.zhang
 */
public final class MimeTypes {

    private static final Logger log = LoggerFactory.getLogger(MimeTypes.class);

    public static final String MIMETYPE_OCTET_STREAM = "application/octet-stream";
    public static final String MIME_TYPE_JSON = "application/json";
    public static final String MIME_TYPE_JSON_UTF8 = MIME_TYPE_JSON + ";charset=UTF-8";
    /**
     * web常用图片扩展名
     */
    public static final String[] WEB_IMAGE_EXTENSIONS = { "jpg","jpeg","png","gif","bmp","ico","svg","svgz","webp",
            "tif","tiff" };
    /**
     * 常用办公文档扩展名(msoffice,openoffice,pdf)
     */
    public static final String[] DOC_EXTENSIONS = { "xls","xlsx","doc","docx","ppt","pptx","pdf","ods","odp","odt",
            "csv" };

    private static MimeTypes mimetypes = null;

    private HashMap<String,String> extToMimetypeMap = new HashMap<String,String>();

    private MimeTypes() {
    }

    /**
     * 实例化并从类路径解析mime.types文件
     */
    public synchronized static MimeTypes getInstance() {
        if (mimetypes != null)
            return mimetypes;

        mimetypes = new MimeTypes();
        // mime.type文件格式同javax.activation.MimeTypeFileTypeMap一样, 所以如果是jdk1.8可以使用该类. 如果是jdk9+使用本类
        InputStream is = mimetypes.getClass()
                .getResourceAsStream("/mime.types");
        if (is != null) {
            try {
                mimetypes.loadAndReplaceMimetypes(is);
            } catch (IOException e) {
                log.error("Failed to load mime types from file in the classpath: mime.types", e);
            } finally {
                IOHelper.closeQuietly(is);
            }
        } else {
            log.warn("Unable to find 'mime.types' file in classpath");
        }
        return mimetypes;
    }

    public void loadAndReplaceMimetypes(InputStream is) throws IOException {
        BufferedReader br = IOHelper.toBufferedReader(is, StandardCharsets.UTF_8);
        String line = null;

        while ((line = br.readLine()) != null) {
            line = line.trim();
            if (line.startsWith("#") || line.length() == 0) {
                // Ignore comments and empty lines.
            } else {
                StringTokenizer st = new StringTokenizer(line, " \t");
                if (st.countTokens() > 1) {
                    String mimetype = st.nextToken();
                    while (st.hasMoreTokens()) {
                        extToMimetypeMap.put(st.nextToken()
                                .toLowerCase(), mimetype);
                    }
                }
            }
        }
    }

    /**
     * 给定文件名返回对应的Mimetype
     * 
     * @param fileName 文件名
     * @return 返回Mimetype,如果无法判断返回application/octet-stream
     */
    public String getMimetype(String fileName) {
        String ext = FileHelper.getExtension(fileName, false);
        if (StringHelper.isNotEmpty(ext)) {
            if (extToMimetypeMap.keySet()
                    .contains(ext)) {
                return extToMimetypeMap.get(ext);
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("Extension '" + ext + "' is unrecognized in mime type listing"
                            + ", using default mime type: '" + MIMETYPE_OCTET_STREAM + "'");
                }
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("File name has no extension, mime type cannot be recognised for: " + fileName);
            }
        }
        return MIMETYPE_OCTET_STREAM;
    }

    /**
     * 根据扩展名查找对应的Mimetype
     * 
     * @param fileExtension 扩展名,不含.号
     * @return 返回Mimetype,如果无法判断返回application/octet-stream
     */
    public String getMimetypeByExt(String fileExtension) {
        if (extToMimetypeMap.keySet()
                .contains(fileExtension)) {
            return extToMimetypeMap.get(fileExtension);
        }
        return MIMETYPE_OCTET_STREAM;
    }

    /**
     * 给定文件名返回对应的Mimetype
     * 
     * @param fileName 文件名
     * @return 返回Optional&lt;Mimetype&gt;,如果判断返回Optional.empty()
     */
    public Optional<String> guessMimetype(String fileName) {
        String ext = FileHelper.getExtension(fileName, false);
        if (StringHelper.isNotEmpty(ext) && extToMimetypeMap.keySet()
                .contains(ext)) {
            return Optional.of(extToMimetypeMap.get(ext));
        }
        return Optional.empty();
    }

    /**
     * 给定文件,根据文件名返回对应的Mimetype
     *
     * @param file
     * @return 返回Mimetype,如果无法判断返回application/octet-stream
     */
    public String getMimetype(File file) {
        return getMimetype(file.getName());
    }

    public void addMimeType(String fileExtension, String mimeType) {
        extToMimetypeMap.put(fileExtension, mimeType);
    }

}